﻿<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
    <script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
    <script type="text/javascript" src="ClientGlobalContext.js.aspx"></script>
    <style>
        * {
            padding: 0;
            margin: 0;
        }


        body {
            color: #000000;
            font-size: 8pt;
            font-family: tahoma;
        }

        #grid {
            width: 100%;
            background-color: #fff;
            border-spacing: 0;
            border-collapse: collapse;
        }

            #grid thead {
                padding: 0;
                margin: 0;
            }

                #grid thead tr {
                    background-color: #F1F4F7;
                }

                    #grid thead tr td {
                        background-color: #F1F4F7;
                        height: 22px;
                        border-bottom: 1px solid #A5ACB5;
                    }

                        #grid thead tr td:last-child {
                            border-right: none;
                        }

                    #grid thead tr td {
                        padding-left: 2px;
                        border-right: 1px solid #A5ACB5;
                    }

            #grid tbody tr {
                height: 26px;
            }

                #grid tbody tr td {
                    border-bottom: 1px solid #DBDEE1;
                    vertical-align: middle;
                }

                #grid tbody tr td {
                    padding: 2px;
                }

        button {
            background-image: url('http://mscrm/Markuphub/WebResources/new_ButtonNormalGradient');
            background-repeat: no-repeat;
            border: 1px #666666 solid;
            line-height: 18px;
            height: 20px;
            width: 84px;
            text-align: center;
            cursor: pointer;
            border-width: 1px;
            border-style: solid;
            background-repeat: repeat-x;
            padding-left: 5px;
            padding-right: 5px;
            font-size: 8pt;
        }

            button:hover {
                background-image: url('http://mscrm/Markuphub/WebResources/new_ButtonSelectedGradient');
            }

        .inputfields {
            font-size: 8pt;
            font-family: tahoma;
            width: 100%;
            height: 19px;
            border: 1px solid #6699CC;
        }
    </style>
    <script>


        var entity = new Object();
        var entityName = "Account";
        var attr = "Name,Description,WebSiteURL";
        var PK = "AccountId";
        var grid = $("#grid");

        //load grid..
        $(function () {
            CreateColumns();
            Get();
        });

        //Edit Record
        $('button[data-role=edit]').live('click', function () {
            console.log("Edit button event fired.");
            if ($(this).text() == "Edit") {
                $.each($("#grid tbody tr[data-rowid=" + $(this).attr("data-rowid") + "]").find("td"), function (i) {
                    if (i < attr.split(',').length) {
                        var _t = $(this).text(); $(this).empty();
                        $(this).append('<input tabindex="0" type="text" class="inputfields" value="' + _t + '" />');
                    }
                });
                console.log("Row edit mode enable..");
                $(this).text("Update");
                // delete button (cancel mode enable)
                $("#grid tbody tr[data-rowid=" + $(this).attr("data-rowid") + "]").find("td:last").find('button[data-role=delete]').text("Cancel");
                console.log("Delete button (cancel mode) enable.");

            } else {

                //update record..
                $.each($("#grid tbody tr[data-rowid=" + $(this).attr("data-rowid") + "]").find("td"), function (i) {
                    if (i < attr.split(',').length) {
                        var _t = $(this).find("input").val(); $(this).empty();
                        $(this).text(_t);
                    }
                });


                entity = new Object();

                for (var i = 0; i < attr.split(',').length; i++) {
                    var _attr = attr.split(',')[i].toString();
                    entity[_attr] = $("#grid tbody tr[data-rowid=" + $(this).attr("data-rowid") + "] td:nth-child(" + (i + 1) + ")").text();
                }
                var id = $(this).attr("data-rowid");
                Serenic.Client.updateRecord(
                                         id,
                                         entity,
                                         entityName,
                                         function () {
                                             console.log("The record was updated");
                                         },
                                         errorHandler
                                       );

                console.log("Row edit mode disable..");
                $(this).text("Edit");
                // delete button (cancel mode disable)
                $("#grid tbody tr[data-rowid=" + $(this).attr("data-rowid") + "]").find("td:last").find('button[data-role=delete]').text("Delete");
                console.log("Delete button (cancel mode) disable.");
            }

        });

        //Delete Record
        $('button[data-role=delete]').live('click', function () {
            //Delete
            console.log("Delete button event fired.");
            if ($(this).text() == "Delete") {
                var result = confirm("Do you want to delete this record?");
                if (result == true) {

                    var id = $(this).attr("data-rowid");

                    Serenic.Client.deleteRecord(
                     id,
                     entityName,
                     function (result) {
                         console.log("The record named" + result.Name + " was updated.");
                         Get();
                     },
                     errorHandler
                   );
                }
            } else {
                console.log("Delete button (cancel mode) disable.");
                $.each($("#grid tbody tr[data-rowid=" + $(this).attr("data-rowid") + "]").find("td"), function (i) {
                    if (i < attr.split(',').length) {
                        var _t = $(this).find("input").val(); $(this).empty();
                        $(this).text(_t);
                    }
                });
                //Cancel mode disable
                $(this).text("Delete");
                $("#grid tbody tr[data-rowid=" + $(this).attr("data-rowid") + "]").find("td:last").find('button[data-role=edit]').text("Edit");
            }
        });

        //Save Record
        $('button[data-role=save]').live('click', function () {
            for (var i = 0; i < attr.split(',').length; i++) {
                var _attr = attr.split(',')[i].toString();
                entity[_attr] = $("#grid tbody tr[data-rowid=new] td:nth-child(" + (i + 1) + ") input").val();
            }
            console.log("Record Save event fired..");
            // Create entity record
            Serenic.Client.createRecord(
                                    entity,
                                    entityName,
                                    function (result) {
                                        console.log("The record named" + result.Name + " was created");
                                        $.each($("#grid tbody tr[data-rowid=new]").find("td"), function (i) {
                                            $(this).find("input").val("");
                                        });
                                        Get();
                                    },
                                    errorHandler
                                  );
        });

        //Create grid columns
        function CreateColumns() {
            for (var i = 0; i < attr.split(',').length; i++) {
                var _attr = attr.split(',')[i].toString();
                $("#grid thead tr").append("<td>" + _attr + "</td>");
            }

            //$("#grid thead tr").append("<td></td>");

            $("#grid thead tr").append("<td><input type='search'/></td>");
        }

        //search
        $("input[type=search]").live("keyup", function () {
            var term = $(this).val()
            if (term != "") {
                $("#grid tbody>tr").hide();
                $("#grid td").filter(function () {
                    return $(this).text().toLowerCase().indexOf(term) > -1
                }).parent("tr").show();
            }
            else {
                $("#grid tbody>tr").show();
            }
        });

        //Get Records.
        function Get() {
            Serenic.Client.retrieveMultipleRecords("Account",
                "$select=AccountId,Name,Description,WebSiteURL",
                function (results) {
                    $("#grid tbody").empty();
                    var gridBody = $("#grid thead");
                    $('#gridRecords').tmpl(results).appendTo("#grid tbody");
                    var newRecord = '<tr data-rowid="new"><td><input type="text" class="inputfields" name="Name" /></td><td><input class="inputfields" name="Description" /></td><td><input class="inputfields" name="WebSiteURL" /></td><td><button data-role="save">Save</button></td></tr>';
                    $("#grid tbody").append(newRecord);
                },
                errorHandler,
                function () {
                }
            );
        }

        function errorHandler(error) {
            console.log(error.message);
        }

        if (typeof (Serenic) == "undefined")
        { Serenic = { __namespace: true }; }
        Serenic.Client = {
            _context: function () {
                ///<summary>
                /// Private function to the context object.
                ///</summary>
                ///<returns>Context</returns>
                if (typeof GetGlobalContext != "undefined")
                { return GetGlobalContext(); }
                else {
                    if (typeof Xrm != "undefined") {
                        return Xrm.Page.context;
                    }
                    else { throw new Error("Context is not available."); }
                }
            },
            _getServerUrl: function () {
                ///<summary>
                /// Private function to return the server URL from the context
                ///</summary>
                ///<returns>String</returns>
                var serverUrl = this._context().getServerUrl()
                if (serverUrl.match(/\/$/)) {
                    serverUrl = serverUrl.substring(0, serverUrl.length - 1);
                }
                return serverUrl;
            },
            _ODataPath: function () {
                ///<summary>
                /// Private function to return the path to the REST endpoint.
                ///</summary>
                ///<returns>String</returns>
                return this._getServerUrl() + "/XRMServices/2011/OrganizationData.svc/";
            },
            _errorHandler: function (req) {
                ///<summary>
                /// Private function return an Error object to the errorCallback
                ///</summary>
                ///<param name="req" type="XMLHttpRequest">
                /// The XMLHttpRequest response that returned an error.
                ///</param>
                ///<returns>Error</returns>
                return new Error("Error : " +
                      req.status + ": " +
                      req.statusText + ": " +
                      JSON.parse(req.responseText).error.message.value);
            },
            _dateReviver: function (key, value) {
                ///<summary>
                /// Private function to convert matching string values to Date objects.
                ///</summary>
                ///<param name="key" type="String">
                /// The key used to identify the object property
                ///</param>
                ///<param name="value" type="String">
                /// The string value representing a date
                ///</param>
                var a;
                if (typeof value === 'string') {
                    a = /Date\(([-+]?\d+)\)/.exec(value);
                    if (a) {
                        return new Date(parseInt(value.replace("/Date(", "").replace(")/", ""), 10));
                    }
                }
                return value;
            },
            _parameterCheck: function (parameter, message) {
                ///<summary>
                /// Private function used to check whether required parameters are null or undefined
                ///</summary>
                ///<param name="parameter" type="Object">
                /// The parameter to check;
                ///</param>
                ///<param name="message" type="String">
                /// The error message text to include when the error is thrown.
                ///</param>
                if ((typeof parameter === "undefined") || parameter === null) {
                    throw new Error(message);
                }
            },
            _stringParameterCheck: function (parameter, message) {
                ///<summary>
                /// Private function used to check whether required parameters are null or undefined
                ///</summary>
                ///<param name="parameter" type="String">
                /// The string parameter to check;
                ///</param>
                ///<param name="message" type="String">
                /// The error message text to include when the error is thrown.
                ///</param>
                if (typeof parameter != "string") {
                    throw new Error(message);
                }
            },
            _callbackParameterCheck: function (callbackParameter, message) {
                ///<summary>
                /// Private function used to check whether required callback parameters are functions
                ///</summary>
                ///<param name="callbackParameter" type="Function">
                /// The callback parameter to check;
                ///</param>
                ///<param name="message" type="String">
                /// The error message text to include when the error is thrown.
                ///</param>
                if (typeof callbackParameter != "function") {
                    throw new Error(message);
                }
            },
            createRecord: function (object, type, successCallback, errorCallback) {
                ///<summary>
                /// Sends an asynchronous request to create a new record.
                ///</summary>
                ///<param name="object" type="Object">
                /// A JavaScript object with properties corresponding to the Schema name of
                /// entity attributes that are valid for create operations.
                ///</param>
                this._parameterCheck(object, "Serenic.Client.createRecord requires the object parameter.");
                ///<param name="type" type="String">
                /// The Schema Name of the Entity type record to create.
                /// For an Account record, use "Account"
                ///</param>
                this._stringParameterCheck(type, "Serenic.Client.createRecord requires the type parameter is a string.");
                ///<param name="successCallback" type="Function">
                /// The function that will be passed through and be called by a successful response. 
                /// This function can accept the returned record as a parameter.
                /// </param>
                this._callbackParameterCheck(successCallback, "Serenic.Client.createRecord requires the successCallback is a function.");
                ///<param name="errorCallback" type="Function">
                /// The function that will be passed through and be called by a failed response. 
                /// This function must accept an Error object as a parameter.
                /// </param>
                this._callbackParameterCheck(errorCallback, "Serenic.Client.createRecord requires the errorCallback is a function.");

                var jsonEntity = window.JSON.stringify(object);

                $.ajax({
                    type: "POST",
                    contentType: "application/json; charset=utf-8",
                    datatype: "json",
                    url: this._ODataPath() + type + "Set",
                    data: jsonEntity,
                    beforeSend: function (xhr) {
                        //Specifying this header ensures that the results will be returned as JSON.             
                        xhr.setRequestHeader("Accept", "application/json");
                    },
                    success: function (data, textStatus, xhr) {
                        successCallback(data.d);
                    },
                    error: function (xhr, textStatus, errorThrown) {
                        errorCallback(Serenic.Client._errorHandler(xhr));
                    }
                });
            },
            retrieveRecord: function (id, type, select, expand, successCallback, errorCallback) {
                ///<summary>
                /// Sends an asynchronous request to retrieve a record.
                ///</summary>
                ///<param name="id" type="String">
                /// A String representing the GUID value for the record to retrieve.
                ///</param>
                this._stringParameterCheck(id, "Serenic.Client.retrieveRecord requires the id parameter is a string.");
                ///<param name="type" type="String">
                /// The Schema Name of the Entity type record to retrieve.
                /// For an Account record, use "Account"
                ///</param>
                this._stringParameterCheck(type, "Serenic.Client.retrieveRecord requires the type parameter is a string.");
                ///<param name="select" type="String">
                /// A String representing the $select OData System Query Option to control which
                /// attributes will be returned. This is a comma separated list of Attribute names that are valid for retrieve.
                /// If null all properties for the record will be returned
                ///</param>
                if (select != null)
                    this._stringParameterCheck(select, "Serenic.Client.retrieveRecord requires the select parameter is a string.");
                ///<param name="expand" type="String">
                /// A String representing the $expand OData System Query Option value to control which
                /// related records are also returned. This is a comma separated list of of up to 6 entity relationship names
                /// If null no expanded related records will be returned.
                ///</param>
                if (expand != null)
                    this._stringParameterCheck(expand, "Serenic.Client.retrieveRecord requires the expand parameter is a string.");
                ///<param name="successCallback" type="Function">
                /// The function that will be passed through and be called by a successful response. 
                /// This function must accept the returned record as a parameter.
                /// </param>
                this._callbackParameterCheck(successCallback, "Serenic.Client.retrieveRecord requires the successCallback parameter is a function.");
                ///<param name="errorCallback" type="Function">
                /// The function that will be passed through and be called by a failed response. 
                /// This function must accept an Error object as a parameter.
                /// </param>
                this._callbackParameterCheck(errorCallback, "Serenic.Client.retrieveRecord requires the errorCallback parameter is a function.");

                var systemQueryOptions = "";

                if (select != null || expand != null) {
                    systemQueryOptions = "?";
                    if (select != null) {
                        var selectString = "$select=" + select;
                        if (expand != null) {
                            selectString = selectString + "," + expand;
                        }
                        systemQueryOptions = systemQueryOptions + selectString;
                    }
                    if (expand != null) {
                        systemQueryOptions = systemQueryOptions + "&$expand=" + expand;
                    }
                }

                $.ajax({
                    type: "GET",
                    contentType: "application/json; charset=utf-8",
                    datatype: "json",
                    url: this._ODataPath() + type + "Set" + "(guid'" + id + "')" + systemQueryOptions,
                    beforeSend: function (xhr) {
                        //Specifying this header ensures that the results will be returned as JSON.             
                        xhr.setRequestHeader("Accept", "application/json");
                    },
                    success: function (data, textStatus, xhr) {
                        //Client does not provide an opportunity to specify a date reviver so this code
                        // parses the xhr.responseText rather than use the data parameter passed by Client.
                        successCallback(JSON.parse(xhr.responseText, Serenic.Client._dateReviver).d);
                    },
                    error: function (xhr, textStatus, errorThrown) {
                        errorCallback(Serenic.Client._errorHandler(xhr));
                    }
                });
            },
            updateRecord: function (id, object, type, successCallback, errorCallback) {
                ///<summary>
                /// Sends an asynchronous request to update a record.
                ///</summary>
                ///<param name="id" type="String">
                /// A String representing the GUID value for the record to retrieve.
                ///</param>
                this._stringParameterCheck(id, "Serenic.Client.updateRecord requires the id parameter.");
                ///<param name="object" type="Object">
                /// A JavaScript object with properties corresponding to the Schema Names for
                /// entity attributes that are valid for update operations.
                ///</param>
                this._parameterCheck(object, "Serenic.Client.updateRecord requires the object parameter.");
                ///<param name="type" type="String">
                /// The Schema Name of the Entity type record to retrieve.
                /// For an Account record, use "Account"
                ///</param>
                this._stringParameterCheck(type, "Serenic.Client.updateRecord requires the type parameter.");
                ///<param name="successCallback" type="Function">
                /// The function that will be passed through and be called by a successful response. 
                /// Nothing will be returned to this function.
                /// </param>
                this._callbackParameterCheck(successCallback, "Serenic.Client.updateRecord requires the successCallback is a function.");
                ///<param name="errorCallback" type="Function">
                /// The function that will be passed through and be called by a failed response. 
                /// This function must accept an Error object as a parameter.
                /// </param>
                this._callbackParameterCheck(errorCallback, "Serenic.Client.updateRecord requires the errorCallback is a function.");

                var jsonEntity = window.JSON.stringify(object);

                $.ajax({
                    type: "POST",
                    contentType: "application/json; charset=utf-8",
                    datatype: "json",
                    data: jsonEntity,
                    url: this._ODataPath() + type + "Set" + "(guid'" + id + "')",
                    beforeSend: function (xhr) {
                        //Specifying this header ensures that the results will be returned as JSON.             
                        xhr.setRequestHeader("Accept", "application/json");
                        //Specify the HTTP method MERGE to update just the changes you are submitting.             
                        xhr.setRequestHeader("X-HTTP-Method", "MERGE");
                    },
                    success: function (data, textStatus, xhr) {
                        //Nothing is returned to the success function
                        successCallback();
                    },
                    error: function (xhr, textStatus, errorThrown) {
                        errorCallback(Serenic.Client._errorHandler(xhr));
                    }
                });
            },
            deleteRecord: function (id, type, successCallback, errorCallback) {
                ///<summary>
                /// Sends an asynchronous request to delete a record.
                ///</summary>
                ///<param name="id" type="String">
                /// A String representing the GUID value for the record to delete.
                ///</param>
                this._stringParameterCheck(id, "Serenic.Client.deleteRecord requires the id parameter.");
                ///<param name="type" type="String">
                /// The Schema Name of the Entity type record to delete.
                /// For an Account record, use "Account"
                ///</param>
                this._stringParameterCheck(type, "Serenic.Client.deleteRecord requires the type parameter.");
                ///<param name="successCallback" type="Function">
                /// The function that will be passed through and be called by a successful response. 
                /// Nothing will be returned to this function.
                /// </param>
                this._callbackParameterCheck(successCallback, "Serenic.Client.deleteRecord requires the successCallback is a function.");
                ///<param name="errorCallback" type="Function">
                /// The function that will be passed through and be called by a failed response. 
                /// This function must accept an Error object as a parameter.
                /// </param>
                this._callbackParameterCheck(errorCallback, "Serenic.Client.deleteRecord requires the errorCallback is a function.");

                $.ajax({
                    type: "POST",
                    contentType: "application/json; charset=utf-8",
                    datatype: "json",
                    url: this._ODataPath() + type + "Set(guid'" + id + "')",
                    beforeSend: function (XMLHttpRequest) {
                        //Specifying this header ensures that the results will be returned as JSON.                 
                        XMLHttpRequest.setRequestHeader("Accept", "application/json");
                        //Specify the HTTP method DELETE to perform a delete operation.                 
                        XMLHttpRequest.setRequestHeader("X-HTTP-Method", "DELETE");
                    },
                    success: function (data, textStatus, xhr) {
                        // Nothing is returned to the success function.
                        successCallback();
                    },
                    error: function (xhr, textStatus, errorThrown) {
                        errorCallback(Serenic.Client._errorHandler(xhr));
                    }
                });
            },
            retrieveMultipleRecords: function (type, options, successCallback, errorCallback, OnComplete) {
                ///<summary>
                /// Sends an asynchronous request to retrieve records.
                ///</summary>
                ///<param name="type" type="String">
                /// The Schema Name of the Entity type records to retrieve
                /// For an Account record, use "Account"
                ///</param>
                this._stringParameterCheck(type, "Serenic.Client.retrieveMultipleRecords requires the type parameter is a string.");
                ///<param name="options" type="String">
                /// A String representing the OData System Query Options to control the data returned
                /// Do not include the $top option, use the top parameters to set the maximum number of records to return.
                ///</param>
                if (options != null)
                    this._stringParameterCheck(options, "Serenic.Client.retrieveMultipleRecords requires the options parameter is a string.");
                ///<param name="successCallback" type="Function">
                /// The function that will be passed through and be called for each page of records returned.
                /// This function should loop through the results and push the records into an array.
                /// </param>
                this._callbackParameterCheck(successCallback, "Serenic.Client.retrieveMultipleRecords requires the successCallback parameter is a function.");
                ///<param name="errorCallback" type="Function">
                /// The function that will be passed through and be called by a failed response. 
                /// This function must accept an Error object as a parameter.
                /// </param>
                this._callbackParameterCheck(errorCallback, "Serenic.Client.retrieveMultipleRecords requires the errorCallback parameter is a function.");
                ///<param name="OnComplete" type="Function">
                /// The function that will be called when all the requested records have been returned.
                /// No parameters are passed to this function.
                /// </param>
                this._callbackParameterCheck(OnComplete, "Serenic.Client.retrieveMultipleRecords requires the OnComplete parameter is a function.");

                var optionsString;
                if (options != null) {
                    if (options.charAt(0) != "?") {
                        optionsString = "?" + options;
                    }
                    else { optionsString = options; }
                }

                $.ajax({
                    type: "GET",
                    contentType: "application/json; charset=utf-8",
                    datatype: "json",
                    url: this._ODataPath() + type + "Set" + optionsString,
                    beforeSend: function (XMLHttpRequest) {
                        //Specifying this header ensures that the results will be returned as JSON.             
                        XMLHttpRequest.setRequestHeader("Accept", "application/json");
                    },
                    success: function (data, textStatus, xhr) {
                        if (data && data.d && data.d.results) {
                            successCallback(JSON.parse(xhr.responseText, Serenic.Client._dateReviver).d.results);
                            if (data.d.__next != null) {
                                var queryOptions = data.d.__next.substring((Serenic.Client._ODataPath() + type + "Set").length);
                                Serenic.Client.retrieveMultipleRecords(type, queryOptions, successCallback, errorCallback, OnComplete);
                            }
                            else { OnComplete(); }
                        }
                    },
                    error: function (xhr, textStatus, errorThrown) {
                        errorCallback(Serenic.Client._errorHandler(xhr));
                    }
                });
            },
            __namespace: true
        };
    </script>
</head>
<body style="padding: 0; margin: 0;">

    <script id="gridRecords" type="text/x-jquery-tmpl">
        <tr data-rowid="${AccountId}">
            <td>${Name}</td>
            <td>${Description}</td>
            <td>${WebSiteURL}</td>
            <td>
                <button tabindex="3" data-rowid="${AccountId}" data-role="edit">Edit</button>
                <button tabindex="4" data-rowid="${AccountId}" data-role="delete">Delete</button></td>
        </tr>
    </script>
    <table id="grid">
        <thead>
            <tr>
            </tr>
        </thead>
        <tbody>
        </tbody>
    </table>
</body>
</html>
